/**
 * 缓存工具（基于内存，刷新会丢失）
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
import { syncLockLoad } from '@/soar';

interface Loader {
  (): Promise<any> | any;
}

interface Config {
  /**
   * 缓存名称，必填
   */
  name: string;
  /**
   * 缓存有效期，默认：-1
   */
  expires?: number;
  /**
   * 缓存加载器
   */
  loader?: Loader;
}

/**
 * 获取有效期
 * @param expires
 */
function getExpires(expires: number | Date) {
  if (typeof expires === 'number') {
    return expires > 0 ? expires * 1000 + new Date().getTime() : -1;
  }
  return expires.getTime();
}

/**
 * 缓存根数据
 */
const CACHE: object = {};

/**
 * 主动清理过期缓存
 */
setInterval(() => {
  const time = new Date().getTime();
  for (const cacheKey in CACHE) {
    const cache = CACHE[cacheKey];
    for (const key in cache) {
      const data = cache[key];
      if (data.e !== -1 && data.e < time) {
        delete cache[key];
      }
    }
  }
}, 5000);

/**
 * 缓存类
 */
export class Cache {
  /**
   * 缓存名称
   * @private
   */
  private readonly name: string;

  /**
   * 缓存数据
   * @private
   */
  private readonly cache: object;

  /**
   * 默认有效期
   * @private
   */
  private readonly expires: number;

  /**
   * 默认加载器
   * @private
   */
  private readonly loader: Loader;

  /**
   * @param config 缓存名称，必填
   */
  constructor(config: Config | string) {
    if (typeof config === 'string') {
      config = { name: config };
    }
    if (!(config.name in CACHE)) {
      CACHE[config.name] = {};
    }
    this.name = config.name;
    this.cache = CACHE[config.name];
    this.expires = config.expires > 0 ? config.expires : -1;
    this.loader = config.loader;
  }

  /**
   * 异步加载数据
   * @param key
   * @param loader 异步加载器
   * @param expires 有效期（单位：秒）
   * @return Promise<any>
   */
  load(key: string, loader: Loader = this.loader, expires: number | Date = this.expires): Promise<any> {
    const data = this.get(key);
    if (data === undefined || data === null) {
      return syncLockLoad(key, loader, (res) => {
        this.put(key, res, expires);
        return res;
      });
    }
    return Promise.resolve(data);
  }

  /**
   * 同步加载数据
   * @param key 键
   * @param supplier 数据提供器
   * @param expires 有效期（单位：秒）
   * @return any
   */
  public compute(key: string, supplier: Function, expires: number | Date = this.expires): any {
    let data = this.get(key);
    if (data === undefined || data === null) {
      data = supplier(key);
      this.put(key, data, expires);
    }
    return data;
  }

  /**
   * 获取缓存
   * @param key 键
   * @param defValue 默认值
   * @return any
   */
  public get(key: string, defValue?: any): any {
    const data = this.cache[key];
    if (data) {
      if (data.e === -1 || data.e > new Date().getTime()) {
        return data.value;
      }
      delete this.cache[key];
    }
    return defValue;
  }

  /**
   * 增加缓存
   * @param key 键
   * @param value 值
   * @param expires 有效期（单位：秒）
   */
  public put(key: string, value: any, expires: number | Date = this.expires): void {
    this.cache[key] = { value, e: getExpires(expires) };
  }

  /**
   * 设置缓存有效期
   * @param key 键
   * @param expires 有效期（单位：秒）
   */
  public expire(key: string, expires?: number | Date): number {
    if (this.cache[key]) {
      if (expires) {
        this.cache[key].e = getExpires(expires);
      }
      return this.cache[key].e;
    }
    return -2;
  }

  /**
   * 删除缓存
   * @param keys 缓存键，支持多个
   */
  public remove(...keys: string[]): void {
    keys.forEach((key) => {
      delete this.cache[key];
    });
  }

  /**
   * 删除所有缓存
   */
  public clear(): void {
    Object.keys(this.cache).forEach((key) => {
      delete this.cache[key];
    });
  }
}

/**
 * 创建缓存
 * @param config 配置：
 *    name 缓存名称，必填
 *    expires 默认有效期，非必填
 *    loader 默认加载器，非必填
 */
export function createCache(config: Config | string) {
  return new Cache(config);
}

/**
 * 默认缓存
 */
export const cache = createCache({ name: 'default' });
